#include <Connectivity/TCPServer.hpp>

TCPServer::TCPServer(std::string& listenAddress, std::string& port) {

	/*
	 * Set port
	 */
	this->listenAddress = listenAddress;
	this->port = port;
	this->connected = 0;

	/*
	 * set default values
	 */
	sockListen = -1;
	serverOpen = true;

	init();
}

int TCPServer::init() {

	int status;

	struct sockaddr_in6 serverInterfaceSockaddr;
	struct addrinfo hints, *res, *p;

	memset(&serverInterfaceSockaddr, 0, sizeof(sockaddr_in6));

	serverOpen = true;

	/* Create IPv6 socket */
	sockListen = socket(AF_INET6, SOCK_STREAM, 0);
	int yes = 1;
	setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

	in6_addr interfaceAddress;
	inet_pton(AF_INET6, listenAddress.c_str(), &interfaceAddress);

	memset(&hints, 0, sizeof hints);
	hints.ai_family = AF_INET6;
	hints.ai_socktype = SOCK_STREAM;


	status = getaddrinfo(listenAddress.c_str(), port.c_str(), &hints, &res);
	if (status) {
		std::cerr << "Error: getaddrinfo " << gai_strerror(status);
		return 1;
	}

	for (p = res; p != NULL; p = p->ai_next) {

		struct sockaddr_in6 *currentAddress;

		currentAddress = (struct sockaddr_in6 *) p->ai_addr;

		if(memcmp(&currentAddress->sin6_addr, &interfaceAddress, sizeof(in6_addr)) == 0){
			serverInterfaceSockaddr = *currentAddress;
		}

	}

	serverInterfaceSockaddr.sin6_scope_id = 4;

	freeaddrinfo(res);

	status = bind(sockListen, (struct sockaddr*) &serverInterfaceSockaddr, sizeof(struct sockaddr_in6));

	/* Check if binding was successful */
	if (status < 0) {
		std::cerr << "Error: Binding " << (int) errno << std::endl;
		return 2;
	}

	/* Start to listen */
	status = listen(sockListen, 1);
	if (status == -1) {
		std::cerr << "Error: Listen" << std::endl;
		return 3;
	}

	int flags = fcntl(sockListen, F_GETFL, 0);
	if (flags == -1) {
		std::cerr << "fcntl()" << std::endl;
		return 4;
	}

	flags |= O_NONBLOCK;
	flags = fcntl(sockListen, F_SETFL, flags);
	if (flags == -1) {
		std::cerr << "fcntl()" << std::endl;
		return 5;
	}

	return 0;

}

TCPConnection *TCPServer::waitForConnection() {

	int clientSocket;
	struct sockaddr_in6 *client = (struct sockaddr_in6*) malloc(sizeof(struct sockaddr_in6));
	socklen_t len = sizeof(struct sockaddr_in6);

	/* Get incoming connection */
	clientSocket = accept(sockListen, (struct sockaddr*) client, &len);

	/* Create a new TCP connection and return it */
	if (clientSocket >= 0) {
		return new TCPConnection(clientSocket, (struct sockaddr*) client);

	} else {
		/*
		 * Something went wrong
		 */
		std::cerr << "Error: Accept" << std::endl;
		return 0;
	}

}

void TCPServer::closeServer() {

	/*
	 * Close listening socket
	 */
	close(sockListen);
	serverOpen = false;

}

int TCPServer::getSock() {
	return sockListen;
}

bool TCPServer::isServerOpen() {
	return serverOpen;
}
